/**
* @author TDSoft
*
* La clase GestorBBDD corresponde a la capa de negocio y se instancia en distintas partes de la aplicacion.
* Se usa para la gestión de la conexión y desconexión de la base de datos
* y para la gestión de las sentencias necesarias para los accesos a la misma.
*
*/
package edu.uoc.tds.pec4.server.bbdd;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.rmi.RemoteException;
import java.util.Properties;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import edu.uoc.tds.pec4.i18n.GestorIdiomas;

public class GestorBBDD {
    /** ConExión de la BBDD. */
    private Connection conexion;
    /** dirección del fichero de propiedades. */
    final String propertiesFile = new File(".").getAbsolutePath() + "/properties/configuration.properties";
    /** DRiver de conexión. */
    final String driver = "org.postgresql.Driver";
    /** variable para indicar si se esta conectad o no. */
    private boolean conectado = false;
    /** sentencia a ejecutar. */
    private Statement sentencia = null;
    private String schema;

    /**
     * Método para obtener una instancia de Connection
     * {pre: una conexión a BBDDD)
     * {post: una instancia de Connection}
     */
    public Connection getConexion(){
        return this.conexion;
    }

    /**
     * Método para obtener el schema de la base de datos
     * {pre: una conexión a BBDDD)
     * {post: esquema de la base de datos}
     */
    public String getSchema(){
        return this.schema;
    }

    /**
     * Método para saber si ya hemos conectado o no
     * {pre: una conexión a BBDDD)
     * {post: verdadero si ya está conectado, false en caso contrario}
     */
    public boolean isConectado(){
        return this.conectado;
    }
    
    /**
     * Método para establecer la conexión e indicar si existe conexión
     * {post: un booleano que indica si se ha conectado o no }
     */
    public boolean conectaBBDD() throws RemoteException{
        String url;
        String usuario;
        String clave;
        
        try {
            if (!conectado){
                //Recoger el driver JDBC especifico de Postgre
                Class.forName(driver);
                //Recuperar información del fichero de propiedades
                Properties prop = new Properties();
                prop.load(new FileInputStream(propertiesFile));

                url = prop.getProperty("url");
                schema = prop.getProperty("schema");
                usuario = prop.getProperty("username");
                clave = prop.getProperty("password");
                this.conexion = DriverManager.getConnection(url, usuario, clave);
                this.conectado = true;
            }
        }
        /** Capturamos el error en que el caso que no se encuentre una clase, en este caso el driver JDBC*/
        catch (ClassNotFoundException exc) {
            throw new RemoteException(GestorIdiomas.getMessage("error.gestorbbdd.driver.jdbc"));
        }
        /** Capturamos el error en que el caso de que el fichero de propiedades no exista */
        catch (FileNotFoundException exc) {
            throw new RemoteException(GestorIdiomas.getMessage("error.gestorbbdd.no.fichero.properties"));
        }
        /** Capturamos el error en el caso de acceso IO */
        catch (IOException exc) {
            throw new RemoteException(GestorIdiomas.getMessage("error.gestorbbdd.accesoIO"));
        }
        /** Capturamos el error en el acceso a la BBDD */
        catch (SQLException exc) {
            throw new RemoteException(GestorIdiomas.getMessage("error.gestorbbdd.bbdd"));
        }
        /** Capturamos excepción de carácter general*/
        catch (Exception exc) {
            throw new RemoteException(GestorIdiomas.getMessage("error.general"));
        }
        return conectado;
    }
    
    /**
     * Método para desconectar e indicar si se ha realizado la acción correctamente
     * {pre: una conexión a BBDDD)
     * {post: un booleano que indica si NO se está conectado }
     */
    public void desconectaBBDD() throws RemoteException {
        try {
            if (conectado){
                this.conexion.close();
                conectado = false;
            }
        }
        /** Capturamos el error en el acceso a la BBDD */
        catch (SQLException exc) {
            throw new RemoteException(GestorIdiomas.getMessage("error.gestorbbdd.bbdd"));
        }
        /** Capturamos excepción de carácter general*/
        catch (Exception exc) {
            throw new RemoteException(GestorIdiomas.getMessage("error.general"));
        }
    }

    /**
     * Método para crear una instancia que crea un objeto de tipo Statement
     * {pre: una conexión a BBDDD)
     * {post: un objeto de tipo Statement }
     */
    public Statement crearSentencia() throws RemoteException{
        Statement sentencia = null;
        try{
            sentencia = this.conexion.createStatement();
        }
        /** Capturamos el error en el acceso a la BBDD */
        catch (SQLException exc) {
            throw new RemoteException(GestorIdiomas.getMessage("error.gestorbbdd.bbdd"));
        }
        /** Capturamos excepción de carácter general*/
        catch (Exception exc) {
            throw new RemoteException(GestorIdiomas.getMessage("error.general"));
        }
        return sentencia;
    }

    /**
     * Método para crear una instancia que CIERRA un objeto de tipo Statement
     * {pre: un instancia de tipo STatement creada)
     * {post: es un método que no devuelve parámetros}
     */
    public void cerrarSentencia() throws RemoteException{
        try{
            sentencia.close(); //cierra la sentencia
        }
        /** Capturamos el error en el acceso a la BBDD */
        catch (SQLException exc) {
            throw new RemoteException(GestorIdiomas.getMessage("error.gestorbbdd.bbdd"));
        }
        /** Capturamos excepción de carácter general*/
        catch (Exception exc) {
            throw new RemoteException(GestorIdiomas.getMessage("error.general"));
        }
    }

    /**
     * Método para crear una instancia que crea un objeto de tipo PreparedStatement
     * {pre: un instancia de tipo Statement creada)
     * {post: devuelve una instancia de PreparedStatement
     *   que se utiliza para ejecutar código evitando ataques de SQLInjection}
     */
    public PreparedStatement prepararSentencia(String statement) throws RemoteException{
        PreparedStatement preparedStatement = null;
        
        try{
            preparedStatement = conexion.prepareStatement(statement, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
        }
        /** Capturamos el error en el acceso a la BBDD */
        catch (SQLException exc) {
            throw new RemoteException(GestorIdiomas.getMessage("error.gestorbbdd.bbdd"));
        }
        /** Capturamos excepción de carácter general*/
        catch (Exception exc) {
            throw new RemoteException(GestorIdiomas.getMessage("error.general"));
        }
        return preparedStatement;
    }
}
